import tkinter as tk
from tkinter import PhotoImage, messagebox, ttk
from typing import Dict, Optional
from PIL import Image, ImageTk

from config import AppConstants
from utils import open_image_file_dialog

class ImageProcessorUI:
    def __init__(self, root):
        # 初始化标志
        self.resize_height_entry = None
        self.resize_width_entry = None
        self.resize_panel = None
        self.rotate_angle_entry = None
        self.rotate_direction_var = None
        self.rotate_panel = None
        self.grayscale_panel = None
        self.crop_rect: Optional[tuple] = None
        self.crop_start_x: Optional[int] = None
        self.crop_start_y: Optional[int] = None
        self.crop_borders: Dict[str, Optional[tk.Label]] = {
            'top': None,
            'bottom': None,
            'left': None,
            'right': None
        }
        self.cell_count_min_cell_size = None
        self.preview_image = None
        self.is_cleaned = False
        self.crop_events_bound = False
        # 初始化 UI 组件变量
        self.root = root
        self.PREVIEW_HEIGHT = None
        # 固定中间和底部区域的高度（初始设为默认值）
        self.NOTEBOOK_HEIGHT = 200
        self.BUTTON_FRAME_HEIGHT = 40
        # app 由 setup_ui 设置
        self.app = None

    def setup_ui(self, app):
        self.app = app
        self.PREVIEW_HEIGHT = app.PREVIEW_HEIGHT
        # 创建顶部图像显示区域
        self._create_top_image_display()
        # 创建中间操作参数面板区域
        self._create_middle_control_panels()
        # 创建底部按钮区域
        self._create_bottom_button_panel()
        self.create_placeholder()

    def _create_top_image_display(self):
        """创建顶部图像显示区域"""
        self.image_container = tk.Frame(self.root)
        self.image_container.pack(pady=10, fill=tk.BOTH, expand=True)
        # 设置固定高度为窗口高度的 PREVIEW_AREA_RATIO
        self.image_container.config(height=int(self.root.winfo_screenheight() * AppConstants.PREVIEW_AREA_RATIO))
        self.image_container.pack_propagate(False)
        # 创建一个内部 Frame 用于居中显示图片，占 image_container 的 90%
        self.center_frame = tk.Frame(self.image_container)
        self.center_frame.place(relx=0.5, rely=0.5, anchor="center", relwidth=1.0, relheight=AppConstants.PREVIEW_IMAGE_RATIO)
        self.image_label = tk.Label(self.center_frame)
        self.image_label.pack(expand=True)

    def _create_middle_control_panels(self):
        """创建中间操作参数面板区域"""
        self.notebook = ttk.Notebook(self.root)
        self.notebook.pack(fill=tk.X)
        self.notebook.config(height=self.app.CONTROL_PANEL_HEIGHT)
        self.notebook.bind("<<NotebookTabChanged>>", self.on_tab_changed)
        self.panels = {}  # 存储面板的字典
        # 创建各个处理面板
        self.create_brightness_contrast_panel()
        self.create_rotate_panel()
        self.create_mirror_panel()
        self.create_resize_panel()
        self.create_crop_panel()
        self.create_grayscale_panel()
        self.create_equalize_panel()
        self.create_subtract_panel()
        self.create_gamma_panel()
        self.create_smoothing_panel()
        self.create_morphology_panel()
        self.create_cell_count_panel()

    def _create_bottom_button_panel(self):
        """创建底部按钮区域"""
        button_frame = tk.Frame(self.root, height=self.app.BUTTON_PANEL_HEIGHT)
        button_frame.pack_propagate(False)
        button_frame.pack(fill=tk.X, pady=10)
        self.load_button = tk.Button(button_frame, text="选择图片")
        self.load_button.pack(side=tk.LEFT, padx=5)
        self.load_button.config(command=self.on_load_image)
        self.save_button = tk.Button(button_frame, text="保存图片")
        self.save_button.pack(side=tk.LEFT, padx=5)
        self.save_button.config(command=self.on_save_image)
        self.undo_button = tk.Button(
            button_frame,
            text="撤销",
            command=self.on_undo_action
        )
        self.undo_button.pack(side=tk.LEFT, padx=5)

    def create_placeholder(self):
        """创建未选择图片时的占位区域"""
        if self.PREVIEW_HEIGHT is None:
            return
        placeholder_width = int(self.PREVIEW_HEIGHT * 1.5)
        # 创建一个Canvas作为占位符
        self.placeholder_canvas = tk.Canvas(
            self.center_frame,
            bg='#f0f0f0',
            height=self.PREVIEW_HEIGHT,
            width=placeholder_width
        )
        # 居中显示
        self.placeholder_canvas.pack(expand=True)
        # 添加提示文本 - 自动居中
        self.placeholder_canvas.create_text(
            placeholder_width // 2,
            self.PREVIEW_HEIGHT // 2,
            text="请选择图片",
            font=('Arial', 16),
            fill='#666666'
        )
        # 标记当前是否显示占位符
        self.showing_placeholder = True

    # ==================== 面板创建方法 ====================

    def create_brightness_contrast_panel(self):
        """创建亮度与对比度调整面板"""
        self.brightness_contrast_panel = tk.Frame(self.notebook)
        self.notebook.add(self.brightness_contrast_panel, text="亮度/对比度")
        self.panels["BrightnessContrast"] = self.brightness_contrast_panel
        # 提示信息
        instruction_label = tk.Label(
            self.brightness_contrast_panel,
            text="拖动滑块以预览处理后的效果",
            font=('Arial', 10),
            fg='gray'
        )
        instruction_label.pack(pady=10)
        # 亮度调节
        brightness_frame = tk.Frame(self.brightness_contrast_panel)
        brightness_frame.pack(pady=10)
        tk.Label(brightness_frame, text="亮   度").pack(side=tk.LEFT, padx=5)
        self.brightness_scale = tk.Scale(
            brightness_frame,
            from_=0.05,
            to=2.0,
            resolution=0.05,
            orient=tk.HORIZONTAL,
            length=300,
            command=self.on_preview_brightness_contrast
        )
        self.brightness_scale.set(1.0)
        self.brightness_scale.pack(side=tk.LEFT)
        # 对比度调节
        contrast_frame = tk.Frame(self.brightness_contrast_panel)
        contrast_frame.pack(pady=10)
        tk.Label(contrast_frame, text="对比度").pack(side=tk.LEFT, padx=5)
        self.contrast_scale = tk.Scale(
            contrast_frame,
            from_=0.05,
            to=2.0,
            resolution=0.05,
            orient=tk.HORIZONTAL,
            length=300,
            command=self.on_preview_brightness_contrast
        )
        self.contrast_scale.set(1.0)
        self.contrast_scale.pack(side=tk.LEFT)
        # 创建一个 Frame 容器用于放置按钮
        button_frame = tk.Frame(self.brightness_contrast_panel)
        button_frame.pack(pady=10)
        # 应用处理按钮
        apply_button = tk.Button(
            button_frame,
            text="应用处理",
            command=lambda: (
                self.on_process_image("BrightnessContrast"),
                self.reset_brightness_contrast()
            )
        )
        apply_button.pack(side=tk.LEFT, padx=5)
        # 还原参数按钮
        reset_button = tk.Button(
            button_frame,
            text="还原参数",
            command=self.reset_brightness_contrast
        )
        reset_button.pack(side=tk.LEFT, padx=5)


    def create_rotate_panel(self):
        """创建旋转处理面板"""
        self.rotate_panel = tk.Frame(self.notebook)
        self.notebook.add(self.rotate_panel, text="旋转")
        self.panels["Rotate"] = self.rotate_panel
        # 旋转方向选择
        direction_frame = tk.Frame(self.rotate_panel)
        direction_frame.pack(pady=5)
        tk.Label(direction_frame, text="方向:").pack(side=tk.LEFT, padx=5)
        self.rotate_direction_var = tk.StringVar(value="Clockwise")
        tk.Radiobutton(direction_frame, text="逆时针",
                       variable=self.rotate_direction_var,
                       value="Clockwise").pack(side=tk.LEFT, padx=5)
        tk.Radiobutton(direction_frame, text="顺时针",
                       variable=self.rotate_direction_var,
                       value="Counterclockwise").pack(side=tk.LEFT, padx=5)
        # 旋转角度输入
        angle_frame = tk.Frame(self.rotate_panel)
        angle_frame.pack(pady=5)
        tk.Label(angle_frame, text="角度:").pack(side=tk.LEFT, padx=5)
        self.rotate_angle_entry = tk.Entry(angle_frame, width=10)
        self.rotate_angle_entry.insert(0, "90")
        self.rotate_angle_entry.pack(side=tk.LEFT)
        # 旋转处理按钮
        tk.Button(
            self.rotate_panel,
            text="应用旋转处理",
            command=lambda: self.on_process_image("Rotate")
        ).pack(pady=10)

    def create_mirror_panel(self):
        """创建镜像操作面板"""
        self.mirror_panel = tk.Frame(self.notebook)
        self.notebook.add(self.mirror_panel, text="镜像操作")
        self.panels["Mirror"] = self.mirror_panel
        # 镜像方向选择
        direction_frame = tk.Frame(self.mirror_panel)
        direction_frame.pack(pady=10)
        tk.Label(direction_frame, text="方向").pack(side=tk.LEFT, padx=5)
        self.mirror_direction_var = tk.StringVar(value="horizontal")
        directions = ["horizontal", "vertical"]
        for direction in directions:
            tk.Radiobutton(
                direction_frame,
                text={"horizontal": "水平镜像", "vertical": "垂直镜像"}[direction],
                variable=self.mirror_direction_var,
                value=direction
            ).pack(side=tk.LEFT, padx=5)
        # 应用按钮
        process_btn = tk.Button(
            self.mirror_panel,
            text="应用镜像操作",
            command=lambda: self.on_process_image("Mirror")
        )
        process_btn.pack(pady=10)

    def create_resize_panel(self):
        """创建缩放处理面板"""
        self.resize_panel = tk.Frame(self.notebook)
        self.notebook.add(self.resize_panel, text="缩放")
        self.panels["Resize"] = self.resize_panel
        # 宽度设置
        width_frame = tk.Frame(self.resize_panel)
        width_frame.pack(pady=5)
        tk.Label(width_frame, text="宽度:").pack(side=tk.LEFT, padx=5)
        self.resize_width_entry = tk.Entry(width_frame, width=10)
        self.resize_width_entry.insert(0, "200")
        self.resize_width_entry.pack(side=tk.LEFT)
        # 高度设置
        height_frame = tk.Frame(self.resize_panel)
        height_frame.pack(pady=5)
        tk.Label(height_frame, text="高度:").pack(side=tk.LEFT, padx=5)
        self.resize_height_entry = tk.Entry(height_frame, width=10)
        self.resize_height_entry.insert(0, "200")
        self.resize_height_entry.pack(side=tk.LEFT)
        # 缩放处理按钮
        tk.Button(
            self.resize_panel,
            text="应用缩放处理",
            command=lambda: (
                self.on_process_image("Resize"),
                # 更新缩放参数
                self.update_resize_parameters()
            )
        ).pack(pady=10)

    def create_crop_panel(self):
        """创建裁剪处理面板"""
        self.crop_panel = tk.Frame(self.notebook)
        self.notebook.add(self.crop_panel, text="裁剪")
        self.panels["Crop"] = self.crop_panel
        # 提示信息
        instruction_label = tk.Label(
            self.crop_panel,
            text="请在图像上拖动鼠标选择裁剪区域",
            font=('Arial', 10),
            fg='gray'
        )
        instruction_label.pack(pady=10)
        # 应用裁剪按钮
        tk.Button(
            self.crop_panel,
            text="应用裁剪处理",
            command=lambda: (
                self.on_process_image("Crop"),
                self.cleanup_crop_selection(),
                # 更新缩放参数
                self.update_resize_parameters()
            )
        ).pack(pady=10)


    def create_grayscale_panel(self):
        """创建灰度处理面板"""
        self.grayscale_panel = tk.Frame(self.notebook)
        self.notebook.add(self.grayscale_panel, text="灰度处理")
        self.panels["Grayscale"] = self.grayscale_panel
        # 灰度处理按钮
        process_btn = tk.Button(
            self.grayscale_panel,
            text="应用灰度处理",
            command=lambda: self.on_process_image("Grayscale")
        )
        process_btn.pack(pady=10)

    def create_equalize_panel(self):
        """创建直方图均衡化面板"""
        self.equalize_panel = tk.Frame(self.notebook)
        self.notebook.add(self.equalize_panel, text="直方图均衡化")
        self.panels["Equalize"] = self.equalize_panel
        # 直方图均衡化按钮
        process_btn = tk.Button(
            self.equalize_panel,
            text="应用直方图均衡化",
            command=lambda: self.on_process_image("Equalize")
        )
        process_btn.pack(pady=10)

    def create_subtract_panel(self):
        """创建数字减影处理面板"""
        self.subtract_panel = tk.Frame(self.notebook)
        self.notebook.add(self.subtract_panel, text="数字减影")
        self.panels["Subtract"] = self.subtract_panel
        # 提示标签
        label = tk.Label(self.subtract_panel, text="点击按钮选择参考图像", font=('Arial', 10))
        label.pack(pady=5)
        # 参考图像路径显示
        self.subtract_ref_path_var = tk.StringVar()
        path_entry = tk.Entry(self.subtract_panel, textvariable=self.subtract_ref_path_var, width=40)
        path_entry.pack(pady=5)
        # 浏览参考图像按钮
        browse_btn = tk.Button(
            self.subtract_panel,
            text="浏览",
            command=self.on_browse_reference
        )
        browse_btn.pack(pady=5)
        # 应用减影处理按钮
        process_btn = tk.Button(
            self.subtract_panel,
            text="应用数字减影",
            command=lambda: self.on_process_image("Subtract")
        )
        process_btn.pack(pady=10)

    def create_gamma_panel(self):
        """创建伽马变换面板"""
        self.gamma_panel = tk.Frame(self.notebook)
        self.notebook.add(self.gamma_panel, text="伽马变换")
        self.panels["Gamma"] = self.gamma_panel
        # 伽马值输入
        gamma_frame = tk.Frame(self.gamma_panel)
        gamma_frame.pack(pady=10)
        tk.Label(gamma_frame, text="伽马值").pack(side=tk.LEFT, padx=5)
        self.gamma_entry = tk.Entry(gamma_frame, width=10)
        self.gamma_entry.insert(0, "1.0")  # 默认伽马值为1.0
        self.gamma_entry.pack(side=tk.LEFT)
        tk.Label(gamma_frame, text="（0.1 ~ 10）").pack(side=tk.LEFT, padx=5)
        # 应用按钮
        process_btn = tk.Button(
            self.gamma_panel,
            text="应用伽马变换",
            command=lambda: self.on_process_image("Gamma")
        )
        process_btn.pack(pady=10)

    def create_smoothing_panel(self):
        """创建平滑滤波面板"""
        self.smoothing_panel = tk.Frame(self.notebook)
        self.notebook.add(self.smoothing_panel, text="平滑滤波")
        self.panels["Smoothing"] = self.smoothing_panel
        # 方法选择
        method_frame = tk.Frame(self.smoothing_panel)
        method_frame.pack(pady=10)
        tk.Label(method_frame, text="滤波方法").pack(side=tk.LEFT, padx=5)
        self.smoothing_method_var = tk.StringVar(value="average")
        methods = ["average", "median", "gaussian"]
        for method in methods:
            tk.Radiobutton(
                method_frame,
                text={"average": "均值滤波", "median": "中值滤波", "gaussian": "高斯滤波"}[method],
                variable=self.smoothing_method_var,
                value=method
            ).pack(side=tk.LEFT, padx=5)
        # 核大小调节
        kernel_frame = tk.Frame(self.smoothing_panel)
        kernel_frame.pack(pady=10)
        tk.Label(kernel_frame, text="核大小").pack(side=tk.LEFT, padx=5)
        self.kernel_scale = tk.Scale(
            kernel_frame,
            from_=3,
            to=21,
            resolution=2,
            orient=tk.HORIZONTAL,
            length=300
        )
        self.kernel_scale.set(3)
        self.kernel_scale.pack(side=tk.LEFT)
        # 应用按钮
        process_btn = tk.Button(
            self.smoothing_panel,
            text="应用平滑滤波",
            command=lambda: self.on_process_image("Smoothing")
        )
        process_btn.pack(pady=10)

    def create_morphology_panel(self):
        """创建形态学操作面板"""
        self.morphology_panel = tk.Frame(self.notebook)
        self.notebook.add(self.morphology_panel, text="形态学操作")
        self.panels["Morphology"] = self.morphology_panel
        # 操作类型选择
        operation_frame = tk.Frame(self.morphology_panel)
        operation_frame.pack(pady=10)
        tk.Label(operation_frame, text="操作类型").pack(side=tk.LEFT, padx=5)
        self.morphology_operation_var = tk.StringVar(value="erosion")
        operations = ["erosion", "dilation", "opening", "closing"]
        for op in operations:
            tk.Radiobutton(
                operation_frame,
                text={"erosion": "腐蚀", "dilation": "膨胀", "opening": "开运算", "closing": "闭运算"}[op],
                variable=self.morphology_operation_var,
                value=op
            ).pack(side=tk.LEFT, padx=5)
        # 核大小调节
        kernel_frame = tk.Frame(self.morphology_panel)
        kernel_frame.pack(pady=10)
        tk.Label(kernel_frame, text="核大小").pack(side=tk.LEFT, padx=5)
        self.morphology_kernel_scale = tk.Scale(
            kernel_frame,
            from_=3,
            to=21,
            resolution=2,
            orient=tk.HORIZONTAL,
            length=300
        )
        self.morphology_kernel_scale.set(3)
        self.morphology_kernel_scale.pack(side=tk.LEFT)
        # 应用按钮
        process_btn = tk.Button(
            self.morphology_panel,
            text="应用形态学操作",
            command=lambda: self.on_process_image("Morphology")
        )
        process_btn.pack(pady=10)

    def create_cell_count_panel(self):
        """创建细胞计数处理面板"""
        self.cell_count_panel = tk.Frame(self.notebook)
        self.notebook.add(self.cell_count_panel, text="细胞计数")
        self.panels["CellCount"] = self.cell_count_panel
        # 应用细胞计数处理按钮
        process_btn = tk.Button(
            self.cell_count_panel,
            text="应用细胞计数",
            command=lambda: self.on_process_image("CellCount")
        )
        process_btn.pack(pady=10)

    # ==================== 事件处理方法 ====================

    def on_tab_changed(self, _=None):
        """当Notebook标签页切换时更新裁剪事件绑定"""
        current_tab = self.notebook.tab(self.notebook.select(), "text")
        if current_tab == "裁剪":
            if not self.crop_events_bound:
                self.image_label.bind("<Button-1>", self.on_drag_start)
                self.image_label.bind("<B1-Motion>", self.on_drag)
                self.image_label.bind("<ButtonRelease-1>", self.on_crop_end)
                self.crop_events_bound = True
        else:
            # 切换到其他面板：解绑裁剪事件，并清理裁剪状态
            if self.crop_events_bound:
                self.image_label.unbind("<Button-1>")
                self.image_label.unbind("<B1-Motion>")
                self.image_label.unbind("<ButtonRelease-1>")
                self.crop_events_bound = False
            self.cleanup_crop_selection()

    def on_preview_brightness_contrast(self, _=None):
        """拖动滑块时实时预览亮度和对比度调整效果"""
        if not self.app or not self.app.current_image:
            return
        try:
            brightness_factor = float(self.brightness_scale.get())
            contrast_factor = float(self.contrast_scale.get())
            processor = self.app.processors["BrightnessContrast"]
            preview_img = processor.process(
                self.app.current_image,
                brightness_factor=brightness_factor,
                contrast_factor=contrast_factor
            )
            # 更新预览图像
            self.app.preview_image = preview_img.resize(
                (int(preview_img.width * (self.app.PREVIEW_HEIGHT / self.app.current_image.height)),
                 self.app.PREVIEW_HEIGHT),
                Image.Resampling.LANCZOS
            )
            self.display_image(self.app.preview_image)
        except Exception as e:
            print(f"预览失败: {e}")

    def reset_brightness_contrast(self):
        """将亮度和对比度滑动条恢复为默认值（1.0）"""
        self.brightness_scale.set(1.0)
        self.contrast_scale.set(1.0)
        self.on_preview_brightness_contrast()

    def update_resize_parameters(self):
        """更新缩放参数为当前图像的分辨率"""
        if self.app and self.app.current_image:
            width, height = self.app.current_image.size
            self.resize_width_entry.delete(0, tk.END)
            self.resize_width_entry.insert(0, str(width))
            self.resize_height_entry.delete(0, tk.END)
            self.resize_height_entry.insert(0, str(height))

    def on_drag_start(self, event):
        current_tab = self.notebook.tab(self.notebook.select(), "text")
        if current_tab not in ["裁剪"]:
            return
        # 直接使用事件坐标（相对于label）
        self.crop_start_x = event.x
        self.crop_start_y = event.y

    def on_drag(self, event):
        if self.crop_start_x is None or self.crop_start_y is None:
            return  # 如果未设置起点，直接返回，不执行绘制选区
        x1, y1 = self.crop_start_x, self.crop_start_y
        x2, y2 = event.x, event.y
        x_left = min(x1, x2)
        y_top = min(y1, y2)
        x_right = max(x1, x2)
        y_bottom = max(y1, y2)
        width = x_right - x_left
        height = y_bottom - y_top
        # 如果边框 Label 不存在，则创建它们
        for key in self.crop_borders:
            label = self.crop_borders[key]
            if label is None:
                self.crop_borders[key] = tk.Label(
                    self.image_label,
                    bg='blue',
                    width=0,
                    height=0
                )
        # 更新边框位置
        self.crop_borders['top'].place(x=x_left, y=y_top, width=width, height=2)
        self.crop_borders['bottom'].place(x=x_left, y=y_bottom, width=width, height=2)
        self.crop_borders['left'].place(x=x_left, y=y_top, width=2, height=height)
        self.crop_borders['right'].place(x=x_right, y=y_top, width=2, height=height)

    def on_crop_end(self, event):
        if self.crop_start_x is None or self.crop_start_y is None:
            return  # 如果没有选择起点，则直接返回
        try:
            x1, y1 = self.crop_start_x, self.crop_start_y
            x2, y2 = event.x, event.y
            left = min(x1, x2)
            upper = min(y1, y2)
            right = max(x1, x2)
            lower = max(y1, y2)
            self.crop_rect = (left, upper, right, lower)
        except TypeError as e:
            self.show_error(f"无效的选择，请重新选择: {e}")

    def on_browse_reference(self):
        """打开文件对话框选择参考图像"""
        file_path = open_image_file_dialog()
        if file_path:
            self.subtract_ref_path_var.set(file_path)

    # ==================== 辅助方法 ====================

    def cleanup_crop_selection(self):
        """清理裁剪选择状态"""
        for key in self.crop_borders:
            label = self.crop_borders[key]
            if label is not None:
                label.destroy()
        # 重置边框引用
        self.crop_borders = {
            'top': None,
            'bottom': None,
            'left': None,
            'right': None
        }
        # 重置裁剪区域坐标
        self.crop_rect = None
        self.crop_start_x = None
        self.crop_start_y = None

    def toggle_load_button(self, enable):
        """切换加载按钮状态"""
        if not self.is_cleaned:
            if enable:
                self.load_button.config(text="选择图片", command=self.on_load_image)
            else:
                self.load_button.config(text="重新选择", command=self.on_reload_image)

    def cleanup(self):
        """清理UI资源"""
        if self.is_cleaned:
            return
        self.is_cleaned = True
        # 清除图像显示
        self.clear_image()
        # 解绑所有事件
        for event in ["<Button-1>", "<Key>", "<<NotebookTabChanged>>"]:
            self.root.unbind(event)
            for panel in self.panels.values():
                panel.unbind(event)
        # 清理子控件
        for panel in self.panels.values():
            for child in panel.winfo_children():
                child.destroy()
            panel.destroy()
        self.notebook.destroy()
        self.image_container.destroy()

    def on_load_image(self):
        """加载图片"""
        if not self.is_cleaned and self.app:
            self.app.load_image()
            # 更新缩放参数
            self.update_resize_parameters()

    def on_reload_image(self):
        """重新加载图片"""
        if not self.is_cleaned and self.app:
            self.app.reload_image()

    def on_process_image(self, processor_name):
        """处理图片"""
        if not self.is_cleaned and self.app:
            self.app.process_image(processor_name)

    def on_save_image(self):
        """保存图片"""
        if not self.is_cleaned and self.app:
            self.app.save_image()

    def display_image(self, image: Image.Image):
        """显示图片"""
        if not self.is_cleaned:
            # 如果当前显示的是占位符，先移除它
            if self.showing_placeholder:
                self.placeholder_canvas.pack_forget()
                self.showing_placeholder = False
            # 转换 PIL.Image 为 Tkinter.PhotoImage
            photo: PhotoImage = ImageTk.PhotoImage(image)
            # 更新 Label 图像（并保持引用）
            self.image_label.config(image=photo)
            self.image_label.image = photo  # 防止被垃圾回收
            self.image_label.pack(expand=True)
            # 保存原始 PIL.Image 用于后续处理
            self.preview_image = image

    def clear_image(self):
        """清除显示的图片"""
        if not self.is_cleaned:
            self.image_label.config(image="")
            if hasattr(self.image_label, 'image'):
                self.image_label.image = None
            # 如果没有显示占位符，则重新显示
            if not self.showing_placeholder:
                self.placeholder_canvas.pack(expand=True)
                self.showing_placeholder = True

    def on_undo_action(self):
        """触发撤销操作"""
        if not self.is_cleaned and self.app:
            self.app.undo_last_action()

    @staticmethod
    def show_error(message):
        """显示错误消息"""
        messagebox.showerror("错误", message)

    @staticmethod
    def show_warning(message):
        """显示警告消息"""
        messagebox.showwarning("警告", message)

    @staticmethod
    def show_info(message):
        """显示信息消息"""
        messagebox.showinfo("信息", message)